﻿/* System.Compute: a Computation Framework for .NET/Mono
 * Copyright (C) 2011 Ralf "raboni" Boeckhorst
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 */

using System.Runtime.InteropServices;

namespace System.Compute.OpenCL
{
    public sealed class CommandQueue : ICloneable, IDisposable
    {
        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern IntPtr clCreateCommandQueue(IntPtr context, IntPtr device, CommandQueueProperties properties, ref ErrorCode errcode_ret);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clRetainCommandQueue(IntPtr command_queue);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clReleaseCommandQueue(IntPtr command_queue);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clSetCommandQueueProperty(IntPtr command_queue, CommandQueueProperties properties, bool enable, ref CommandQueueProperties old_properties);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueReadBuffer(IntPtr command_queue, IntPtr buffer, bool blocking_read, SizeT offset, SizeT cb, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueWriteBuffer(IntPtr command_queue, IntPtr buffer, bool blocking_write, SizeT offset, SizeT cb, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueCopyBuffer(IntPtr command_queue, IntPtr src_buffer, IntPtr dst_buffer, SizeT src_offset, SizeT dst_offset, SizeT cb, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueReadBufferRect(IntPtr command_queue, IntPtr buffer, bool blocking_read, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] buffer_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] host_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT buffer_row_pitch, SizeT buffer_slice_pitch, SizeT host_row_pitch, SizeT host_slice_pitch, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueWriteBufferRect(IntPtr command_queue, IntPtr buffer, bool blocking_write, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] buffer_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] host_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT buffer_row_pitch, SizeT buffer_slice_pitch, SizeT host_row_pitch, SizeT host_slice_pitch, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueCopyBufferRect(IntPtr command_queue, IntPtr src_buffer, IntPtr dst_buffer, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] src_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] dst_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT src_row_pitch, SizeT src_slice_pitch, SizeT dst_row_pitch, SizeT dst_slice_pitch, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueReadImage(IntPtr command_queue, IntPtr image, bool blocking_read, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT row_pitch, SizeT slice_pitch, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueWriteImage(IntPtr command_queue, IntPtr image, bool blocking_write, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT row_pitch, SizeT slice_pitch, IntPtr ptr, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueCopyImage(IntPtr command_queue, IntPtr src_image, IntPtr dst_image, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] src_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] dst_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueCopyImageToBuffer(IntPtr command_queue, IntPtr src_image, IntPtr dst_buffer, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] src_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, SizeT dst_offset, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueCopyBufferToImage(IntPtr command_queue, IntPtr src_buffer, IntPtr dst_image, SizeT src_offset, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] dst_origin, [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)] SizeT[] region, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueFillBuffer(IntPtr command_queue, IntPtr buffer, IntPtr pattern, SizeT pattern_size, SizeT offset, SizeT size, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueMarker(IntPtr command_queue, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueMarkerWithWaitList(IntPtr command_queue, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueBarrier(IntPtr command_queue);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueBarrierWithWaitList(IntPtr command_queue, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueWaitForEvents(IntPtr command_queue, uint num_events, IntPtr[] event_list);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueNDRangeKernel(IntPtr command_queue, IntPtr kernel, uint work_dim, SizeT[] global_work_offset, SizeT[] global_work_size, SizeT[] local_work_size, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clEnqueueTask(IntPtr command_queue, IntPtr kernel, uint num_events_in_wait_list, IntPtr[] event_wait_list, ref IntPtr @event);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clFlush(IntPtr command_queue);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clFinish(IntPtr command_queue);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clGetCommandQueueInfo(IntPtr command_queue, CommandQueueInfo param_name, SizeT param_value_size, ref IntPtr param_value, SizeT param_value_size_ret);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clGetCommandQueueInfo(IntPtr command_queue, CommandQueueInfo param_name, SizeT param_value_size, ref uint param_value, SizeT param_value_size_ret);

        [DllImport("opencl.dll", ExactSpelling = true)]
        static extern ErrorCode clGetCommandQueueInfo(IntPtr command_queue, CommandQueueInfo param_name, SizeT param_value_size, ref CommandQueueProperties param_value, SizeT param_value_size_ret);

        public Context Context
        {
            get
            {
                IntPtr ret = IntPtr.Zero;
                ErrorCode res = clGetCommandQueueInfo(Handle, CommandQueueInfo.Context, IntPtr.Size, ref ret, 0);
                if (res != ErrorCode.Success)
                    throw new OpenCLException(res);
                return new Context(ret);
            }
        }

        public Device Device
        {
            get
            {
                IntPtr ret = IntPtr.Zero;
                ErrorCode res = clGetCommandQueueInfo(Handle, CommandQueueInfo.Device, IntPtr.Size, ref ret, 0);
                if (res != ErrorCode.Success)
                    throw new OpenCLException(res);
                return new Device(ret);
            }
        }

        public uint ReferenceCount
        {
            get
            {
                uint ret = 0;
                ErrorCode res = clGetCommandQueueInfo(Handle, CommandQueueInfo.ReferenceCount, sizeof(uint), ref ret, 0);
                if (res != ErrorCode.Success)
                    throw new OpenCLException(res);
                return ret;
            }
        }

        public CommandQueueProperties Properties
        {
            get
            {
                CommandQueueProperties ret = 0;
                ErrorCode res = clGetCommandQueueInfo(Handle, CommandQueueInfo.Properties, sizeof(CommandQueueProperties), ref ret, 0);
                if (res != ErrorCode.Success)
                    throw new OpenCLException(res);
                return ret;
            }
        }

        internal IntPtr Handle;

        internal CommandQueue(IntPtr handle)
        {
            Handle = handle;
        }

        public CommandQueue(Context context, Device device, CommandQueueProperties properties)
        {
            ErrorCode res = ErrorCode.Success;
            Handle = clCreateCommandQueue(context.Handle, device.Handle, properties, ref res);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
        }

        [Obsolete("Deprecated as of OpenCL 1.1")]
        public CommandQueueProperties SetProperty(CommandQueueProperties properties, bool enable)
        {
            CommandQueueProperties old = (CommandQueueProperties)0;
            ErrorCode res = clSetCommandQueueProperty(Handle, properties, enable, ref old);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return old;
        }

        public Event EnqueueReadBuffer<T>(DeviceBuffer buffer, bool blocking, SizeT offset, SizeT cb, ref T @return, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(@return, GCHandleType.Pinned);
            ErrorCode res = clEnqueueReadBuffer(Handle, buffer.Handle, blocking, offset, cb, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            @return = (T)h.Target;
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueWriteBuffer<T>(DeviceBuffer buffer, bool blocking, SizeT offset, SizeT cb, T data, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(data, GCHandleType.Pinned);
            ErrorCode res = clEnqueueWriteBuffer(Handle, buffer.Handle, blocking, offset, cb, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueCopyBuffer(DeviceBuffer src_buffer, DeviceBuffer dst_buffer, SizeT src_offset, SizeT dst_offset, SizeT cb, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueCopyBuffer(Handle, src_buffer.Handle, dst_buffer.Handle, src_offset, dst_offset, cb, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueReadBufferRect<T>(DeviceBuffer buffer, bool blocking, SizeT[] buffer_offset, SizeT[] host_offset, SizeT[] region_size, SizeT src_row_pitch, SizeT src_slice_pitch, SizeT dst_row_pitch, SizeT dst_slice_pitch, ref T @return, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(@return, GCHandleType.Pinned);
            ErrorCode res = clEnqueueReadBufferRect(Handle, buffer.Handle, blocking, buffer_offset, host_offset, region_size, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            @return = (T)h.Target;
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueWriteBufferRect<T>(DeviceBuffer buffer, bool blocking, SizeT[] buffer_offset, SizeT[] host_offset, SizeT[] region_size, SizeT src_row_pitch, SizeT src_slice_pitch, SizeT dst_row_pitch, SizeT dst_slice_pitch, T data, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(data, GCHandleType.Pinned);
            ErrorCode res = clEnqueueWriteBufferRect(Handle, buffer.Handle, blocking, buffer_offset, host_offset, region_size, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueCopyBufferRect<T>(DeviceBuffer src_buffer, DeviceBuffer dst_buffer, SizeT[] src_offset, SizeT[] dst_offset, SizeT[] region_size, SizeT src_row_pitch, SizeT src_slice_pitch, SizeT dst_row_pitch, SizeT dst_slice_pitch, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueCopyBufferRect(Handle, src_buffer.Handle, dst_buffer.Handle, src_offset, dst_offset, region_size, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueReadImage<T>(DeviceImage image, bool blocking, SizeT[] origin, SizeT[] region, SizeT row_pitch, SizeT slice_pitch, ref T @return, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(@return, GCHandleType.Pinned);
            ErrorCode res = clEnqueueReadImage(Handle, image.Handle, blocking, origin, region, row_pitch, slice_pitch, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            @return = (T)h.Target;
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueWriteImage<T>(DeviceImage image, bool blocking, SizeT[] origin, SizeT[] region, SizeT row_pitch, SizeT slice_pitch, T data, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(data, GCHandleType.Pinned);
            ErrorCode res = clEnqueueWriteImage(Handle, image.Handle, blocking, origin, region, row_pitch, slice_pitch, h.AddrOfPinnedObject(), (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueCopyImage(DeviceImage src_image, DeviceImage dst_image, SizeT[] src_origin, SizeT[] dst_origin, SizeT[] region, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueCopyImage(Handle, src_image.Handle, dst_image.Handle, src_origin, dst_origin, region, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueCopyImageToBuffer(DeviceImage src_image, DeviceBuffer dst_buffer, SizeT[] src_origin, SizeT[] region, SizeT dst_offset, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueCopyImageToBuffer(Handle, src_image.Handle, dst_buffer.Handle, src_origin, region, dst_offset, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueCopyBufferToImage(DeviceBuffer src_buffer, DeviceImage dst_image, SizeT src_offset, SizeT[] dst_origin, SizeT[] region, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueCopyBufferToImage(Handle, src_buffer.Handle, dst_image.Handle, src_offset, dst_origin, region, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueFillBuffer<T>(DeviceBuffer buffer, T pattern, SizeT pattern_size, SizeT offset, SizeT size, params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            GCHandle h = GCHandle.Alloc(pattern, GCHandleType.Pinned);
            ErrorCode res = clEnqueueFillBuffer(Handle, buffer.Handle, h.AddrOfPinnedObject(), pattern_size, offset, size, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            h.Free();
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        [Obsolete("Renamed as of OpenCL 1.2 -> use EnqueueMarkerWithWaitList() instead")]
        public Event EnqueueMarker()
        {
            IntPtr @event = IntPtr.Zero;
            ErrorCode res = clEnqueueMarker(Handle, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public Event EnqueueMarkerWithWaitList(params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueMarkerWithWaitList(Handle, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        [Obsolete("Renamed as of OpenCL 1.2 -> use EnqueueBarrierWithWaitList() instead")]
        public void EnqueueBarrier()
        {
            ErrorCode res = clEnqueueBarrier(Handle);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
        }

        public Event EnqueueBarrierWithWaitList(params Event[] event_wait_list)
        {
            IntPtr @event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueBarrierWithWaitList(Handle, (uint)event_wait_list.Length, p_event_wait_list, ref @event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(@event);
        }

        public void EnqueueWaitForEvents(params Event[] event_list)
        {
            IntPtr[] p_event_list = null;
            if (event_list.LongLength > 0)
            {
                p_event_list = new IntPtr[event_list.LongLength];
                for (long i = 0; i < event_list.LongLength; i++)
                    p_event_list[i] = event_list[i].Handle;
            }
            ErrorCode res = clEnqueueWaitForEvents(Handle, (uint)event_list.LongLength, p_event_list);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
        }

        public Event EnqueueNDRangeKernel(Kernel kernel, uint work_dim, SizeT[] global_work_offset, SizeT[] global_work_size, SizeT[] local_work_size, params Event[] event_wait_list)
        {
            IntPtr p_event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueNDRangeKernel(Handle, kernel.Handle, work_dim, global_work_offset, global_work_size, local_work_size, (uint)event_wait_list.LongLength, p_event_wait_list, ref p_event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(p_event);
        }

        public Event EnqueueTask(Kernel kernel, params Event[] event_wait_list)
        {
            IntPtr p_event = IntPtr.Zero;
            IntPtr[] p_event_wait_list = null;
            if (event_wait_list.LongLength > 0)
            {
                p_event_wait_list = new IntPtr[event_wait_list.LongLength];
                for (long i = 0; i < event_wait_list.LongLength; i++)
                    p_event_wait_list[i] = event_wait_list[i].Handle;
            }
            ErrorCode res = clEnqueueTask(Handle, kernel.Handle, (uint)event_wait_list.LongLength, p_event_wait_list, ref p_event);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new Event(p_event);
        }

        public Event EnqueueNativeKernel()
        {
            throw new NotImplementedException();
        }

        public void Flush()
        {
            ErrorCode res = clFlush(Handle);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
        }

        public void Finish()
        {
            ErrorCode res = clFinish(Handle);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
        }

        public object Clone()
        {
            ErrorCode res = clRetainCommandQueue(Handle);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            return new CommandQueue(Handle);
        }

        public void Dispose()
        {
            ErrorCode res = clReleaseCommandQueue(Handle);
            if (res != ErrorCode.Success)
                throw new OpenCLException(res);
            Handle = IntPtr.Zero;
        }
    }
}
